#include "min_list.h"

#define MAX_LINE_LENGTH 256
#define FEATURE float
#define CLUSTER_CENTER float
#define DIM 128
#define FEATURE_FORMAT "%f "

#define ASSERT_NULL_POINTER(ptr, message) \
    do { \
        if ((ptr) == NULL) { \
            printf("[ERROR!]%s in %s is NULL\n", message, __func__); \
            exit(EXIT_FAILURE); \
        } \
    } while(0)

//如果FEATURE是float，则定义L2_DISTANCE为l2_distance_float。否则如果FEATURE是uint8定义L2_DISTANCE为l2_distance_uint8
#if defined(FEATURE) && FEATURE == float
    #define L2_DISTANCE l2_distance_float
#elif defined(FEATURE) && FEATURE == uint8_t
    #define L2_DISTANCE l2_distance_uint8
#else
    #error "Invalid FEATURE type. Please define FEATURE as float or uint8_t."
#endif

//保存配置信息
typedef struct { 
    char dataset_path[MAX_LINE_LENGTH];
    char query_path[MAX_LINE_LENGTH];
    char gt_path[MAX_LINE_LENGTH];
    char cluster_center_path[MAX_LINE_LENGTH];
    char offset_list_path[MAX_LINE_LENGTH];
    char last_layer_path[MAX_LINE_LENGTH];
    int cluster_count;
    double train_ratio;
    int k;
    int nprobe;
    int R;
    int L;
    int B;
    int M;
    int n;
    int dim;

    //user_builder
    int max_query;
    int thread_count;
    int recall_k;
    int real_k;
    int last_layer_nprobe;
} Config;

//用于保存和使用bin文件的结构体
//用void的原因是需要同时用于查询（可能为float或者uint8），gt（通常为uint32）。避免维护2套代码
typedef struct { 
    int n;
    int dim;
    int size; //每个feature的字节数
    void* features;
} Bin;

Bin* init_bin(const char *filename, int feature_size); //传入feature_size让bin知道数据类型
void* get_bin_line(Bin* bin, int index);
void destroy_bin(Bin* bin);
void print_bin(Bin* bin, int index_start, int index_end, char* format_string);

//读取最后一层向量数据。这里没有用bin是因为：
//1.结构是向量id+向量本身，不符合bin结构体的结构，强行兼容将变得更复杂
//2.bin的初始化函数会一口气读取所有数据，不符合最后一层按需读取的需求
//3.独立出来便于后续优化（例如读缓存策略等）

//每个数据集在.h中单独设置是为了降低内存分配和管理的开销
typedef struct {
    int id;
    FEATURE features[DIM];
} LastLayer_Vector;

typedef struct {
    int count;
    LastLayer_Vector* vectors;
} LastLayer_Vectors;

LastLayer_Vectors* create_last_layer_vectors(int count); //传入feature_size的原因是需要匹配不同类型的feature
int load_last_layer_vectors(LastLayer_Vectors* vectors, FILE* fp, int index_start, int count); //一次批量加载多个向量，减少IO次数
void destroy_last_layer_vectors(LastLayer_Vectors* vectors);
void print_last_layer_vectors(LastLayer_Vectors* vectors);

int min(int a, int b);
float l2_distance_float(float* A, float* B, int dim);
float l2_distance(FEATURE* A, FEATURE* B, int dim);

float recall_rate(struct minlist* result, Bin* gts, int id, int k);

void parse_config(const char *filename, Config *config);
void print_config(const Config *config);

//每个请求的结果
typedef struct _Request_Result {
    int id; //请求的id
    unsigned int io_count; //总共进行io的次数（不考虑长度）
    unsigned int io_length; //总共读取的向量数量
    float recall_rate;
    unsigned int io_latency;

    struct _Request_Result* next; //组织为链表形式，便于灵活插入
} Request_Result;

//总结果
typedef struct {
    int count; //包含的请求数量
    unsigned int io_count_sum;
    unsigned int io_length_sum;
    float recall_rate_sum; //召回率的累加，用于计算平均召回率

    Request_Result* result_head; //用于遍历结果
    Request_Result* result_tail; //用于加快插入速度
} Request_Results;

Request_Results* create_result();
void print_result(Request_Results* results, Config* config);
void insert_result(Request_Results* results, Request_Result* result);
void destroy_result(Request_Results* results);

//每层的检索函数
